package org.algorithm;

import org.algorithm.floyd.FloydAlgorithm;

import java.awt.*;
import java.util.*;

public class ReviewAlgorithm {

    public static void main(String[] args) {
        // TODO: 2022/4/12 测试二分查找
        /*int[] arr = {1,3,5,6,7,8,23,43,55,64};
        BinarySearchRecursion binarySearchRecursion = new BinarySearchRecursion();
        System.out.println(binarySearchRecursion.binarySearch(arr, 8));*/

        // TODO: 2022/4/12 测试汉诺塔
       /* DivideAndConquer divideAndConquer = new DivideAndConquer();
        divideAndConquer.hanoiTower('A', 'B', 'C', 3);*/

        // TODO: 2022/4/13 测试动态规划
        int[] wi = {1, 4, 2, 5};
        int[] vi = {200, 300, 400, 300};
        DynamicProgram dp = new DynamicProgram();
        dp.dynamicProgram(vi, wi, 10);

    }
}

/**
 * 二分查找，非递归
 */
class BinarySearch {


    public int binarySearch(int[] arr, int var) {
        //主要是无论是哪种查找，都需要开始和结束坐标
        int l = 0, r = arr.length - 1;
        int mid = (l + r) / 2;

        while (l < r) {

            if (arr[mid] == var) {
                return mid;
            }

            if (arr[mid] > var) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return -1;
    }
}


/**
 * 汉诺塔 三个桩子，将所有盘子都移动到c桩子，一次只能移动一个
 * 如果只有两个盘子：将1从a盘放到b盘，将2从a盘放到c盘，将1从b盘移动到c盘
 * <p>
 * 原理是分到最小维度然后在治理，这里是分成上下两部分，上部分==>B盘，下部分==>C盘， 最后上部分从B盘==>C盘 这就是就是分治的本质，
 */
class DivideAndConquer {

    public void hanoiTower(char a, char b, char c, int i) {

        if (i == 1) {
            System.out.println(a + " == > " + c + "  : " + i);
        } else {

            // TODO: 2022/4/12 这就是上部分，i-1放到b盘，从a==>b， 这部分递归到最后就是选择1是放到b还是c
            hanoiTower(a, c, b, i - 1);
            // TODO: 2022/4/12 就是从a ==> c 最总目的
            System.out.println(a + " == > " + c + "  : " + i);
            // TODO: 2022/4/12 这部分是对应从a==>b处理  然后i-1从b放到==>c
            hanoiTower(b, a, c, i - 1);
        }
    }
}

/**
 * 动态规划 解决给定一个固定容量的背包， 尽可能从已有的几中物品中选择价值综合最高
 * 1.要使用二维数组解决，列数用物品的重量数组w.length+1 行用给定的背包容量+1
 * 2.遍历物品的重量和0-背包容量，每个容量都与1至背包容量依次比较，如果物品重量>背包容量，就让当前位置v[i][j]=v[i-1][j]
 * 否则就取v[i-1][j] 和 vi[i] + v[j-1][j-wi[i]]
 */
class DynamicProgram {

    public void dynamicProgram(int[] vi, int[] wi, int pack) {

        int[][] v = new int[wi.length + 1][pack + 1];
        //将首列置为0，标识背包容量未0时，所有物品都不可放入
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0;
        }

        //将首行全部置为0，表示没有物品时，不可放入背包
        for (int i = 0; i < v[0].length; i++) {
            v[0][i] = 0;
        }
        //输出二维数组
        for (int[] ints : v) {
            System.out.println(Arrays.toString(ints));
        }

        //TODO AAA
        for (int i = 1; i < v.length; i++) {
            for (int j = 1; j < v[i].length; j++) {

                if (j < wi[i - 1]) {
                    v[i][j] = v[i - 1][j];
                } else {
                    //重点是如果当前物品的重量小于包的容量j，那么就让当前物品的价值+剩余容量j-wi[i]的价值
                    v[i][j] = Math.max(v[i - 1][j], vi[i - 1] + v[i - 1][j - wi[i - 1]]);
                }
            }
        }


        System.out.println("==============================");
        for (int[] ints : v) {
            for (int anInt : ints) {
                System.out.printf("%-5d", anInt);
            }
            System.out.println();
        }
    }
}

class Dijkstra {
    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //邻接矩阵
        int[][] matrix = new int[vertex.length][vertex.length];

        matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, N, N, 9, N, N, 3};
        matrix[2] = new int[]{7, N, N, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, N, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, N};

        Dijkstra dijkstra = new Dijkstra(vertex, matrix);
        dijkstra.show();
        dijkstra.djs(2);
        dijkstra.shwoDijkstra();

    }


    final static int N = 65535;// 表示不可以连接
    private char[] vertex;
    private int[][] matrix;
    private int[] already_arr;
    private int[] visited_pre;
    private int[] dis;

    public Dijkstra(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        this.matrix = matrix;

        already_arr = new int[vertex.length];
        visited_pre = new int[vertex.length];
        this.dis = new int[vertex.length];
        Arrays.fill(dis, 65535);
    }

    public void shwoDijkstra() {
        System.out.println(Arrays.toString(already_arr));
        System.out.println(Arrays.toString(visited_pre));
        for (int i = 0; i < vertex.length; i++) {
            if (dis[i] < N) {
                System.out.println("(" + vertex[i] + ")" + dis[i]);
            }
        }

    }

    public void show() {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                System.out.printf("%6d", matrix[i][j]);
            }
            System.out.println();
        }
    }

    public void djs(int index) {
        already_arr[index] = 1;
        dis[index] = 0;
        update(index);
        for (int i = 1; i < vertex.length; i++) {
            index = updateArr();
            update(index);
        }
    }

    public void update(int index) {
        int len;
        for (int i = 0; i < matrix[index].length; i++) {
            len = dis[index] + matrix[index][i];
            if (!in(i) && len < dis[i]) {
                updatePre(i, index);
                updateDis(i, len);
            }
        }
    }

    /**
     * 功能：查看当前顶顶是否已经被访问过
     *
     * @param index 访问顶点索引
     * @return
     */
    public boolean in(int index) {
        return already_arr[index] == 1;
    }

    /**
     * 获取当前节点距离初始节点的最小（前面已经动态更新到最小了）距离
     *
     * @param index
     * @return
     */
    public int getDis(int index) {
        return dis[index];
    }

    /**
     * 功能： 设置pre顶点的前驱为index顶点
     *
     * @param pre
     * @param index
     */
    public void updatePre(int pre, int index) {
        visited_pre[pre] = index;
    }


    /**
     * 更新index顶点到开始顶点的距离
     *
     * @param index
     * @param len
     */
    public void updateDis(int index, int len) {
        dis[index] = len;
    }

    /**
     * 功能: 获取下一个距离初始顶点最近的顶点，继续更新dis[i]中每个节点距离初始节点的距离
     *
     * @return
     */
    public int updateArr() {

        int index = 0, min = 66535;

        for (int i = 0; i < already_arr.length; i++) {
            if (!in(i) && dis[i] < min) {
                min = dis[i];
                index = i;
            }
        }
        already_arr[index] = 1;
        return index;
    }


}

/**
 * 弗洛伊德算法 计算每个节点距离其他所有节点的最近距离
 */
class Floyd {
    private char[] vertex;
    private int[][] matrix;
    private int[][] preVertex;


    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //创建邻接矩阵
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535;
        matrix[0] = new int[]{0, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, 0, N, 9, N, N, 3};
        matrix[2] = new int[]{7, N, 0, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, 0, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};

        Floyd floyd = new Floyd(vertex, matrix);
        floyd.show();
        floyd.algorithm();
        floyd.show();
    }

    public Floyd(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        this.matrix = matrix;
        preVertex = new int[vertex.length][vertex.length];
        for (int i = 0; i < preVertex.length; i++) {
            Arrays.fill(preVertex[i], i);
        }
    }

    public void algorithm() {

        int len;
        //TODO:  遍历中间节点，作用：1.如果开始和结束节点未联通时联通开始节点和结束节点，
        //TODO: 2.如果通过该节点时计算开始和结束节点的最短路径
        for (int k = 0; k < vertex.length; k++) {

            //TODO:  开始节点
            for (int i = 0; i < vertex.length; i++) {
                // TODO: 结束节点
                for (int j = 0; j < vertex.length; j++) {
                    // TODO: 判断 开始和中间节点距离 + 中间节点和结束节点距离是否小于开始和结束节点的距离，如果小于就更新开始和结束节点的距离
                    len = matrix[i][k] + matrix[k][j];
                    if (len < matrix[i][j]) {
                        matrix[i][j] = len;
                        preVertex[i][j] = preVertex[k][j];
                    }
                }
            }
        }
    }

    public void show() {
        for (int i = 0; i < preVertex.length; i++) {
            for (int j = 0; j < preVertex[i].length; j++) {
                System.out.print(vertex[preVertex[i][j]]);
            }
            System.out.println();
        }
    }
}

/**
 * 骑士周游，使用递归
 */
class HorseChessboard {
    //棋盘列数
    private static int X;
    //棋盘行数
    private static int Y;
    //表示是否已经走过
    private static boolean visited[];

    //已经走过全部棋盘
    private static boolean finished = false;

    public static void main(String[] args) {
        System.out.println("骑士周游算法，开始运行~~");
        //测试骑士周游算法是否正确
        X = 10;
        Y = 9;
        int row = 1; //马儿初始位置的行，从1开始编号
        int column = 1; //马儿初始位置的列，从1开始编号
        //创建棋盘
        int[][] chessboard = new int[X][Y];
        visited = new boolean[X * Y];//初始值都是false
        //测试一下耗时
        long start = System.currentTimeMillis();
        traversalChessboard(chessboard, row - 1, column - 1, 1);
        long end = System.currentTimeMillis();
        System.out.println("共耗时: " + (end - start) + " 毫秒");

        //输出棋盘的最后情况
        for (int[] rows : chessboard) {
            for (int step : rows) {
                System.out.print(step + "\t");
            }
            System.out.println();
        }
    }

    /**
     * 功能：
     *
     * @param column 列
     * @param row    行
     * @param step   当前步数
     */
    public static void traversalChessboard(int[][] chessboard, int column, int row, int step) {
        //先将当前访问位置标注为已访问
        visited[Y * row + column] = true;
        //将当前step赋值给棋盘位置
        chessboard[row][column] = step;
        //获取下一步可走路线集合并遍历
        ArrayList<Point> ps = next(new Point(column, row));
        ps.sort(new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                int o1Size = next(o1).size();
                int o2Size = next(o2).size();
                return o1Size - o2Size;
            }
        });
        while (!ps.isEmpty()) {
            Point point = ps.remove(0);
            //判断当前点位未走过，进行递归
            if (!visited[Y * point.y + point.x]) {
                traversalChessboard(chessboard, point.x, point.y, step + 1);
            }
        }
        //判断当前是否没有完成全部的步骤
        if (!finished && step < X * Y) {
            //没有完成就将当前位置设置为未访问过
            visited[Y * row + column] = false;
            chessboard[row][column] = 0;
        } else {
            //如果完成就设置为完成，其他递归全部
            finished = true;
        }
    }

    /**
     * 功能：找到下一步可走的所有路径
     *
     * @param curPoint 棋当前位置
     * @return 返回可走棋点的集合
     */
    public static ArrayList<Point> next(Point curPoint) {
        ArrayList<Point> ps = new ArrayList<>();
        Point p1 = new Point();
        /**
         * 按照当前传入的point为中间位置，分别计算马可以走的所有位置
         */

        //跳到5这个位置的时候
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        //跳到6这个位置的时候
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        //跳到7这个位置的时候
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        //跳到0这个位置的时候
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        //跳到1这个位置的时候
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        //跳到2这个位置的时候
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        //跳到3这个位置的时候
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        //跳到4这个位置的时候
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        return ps;
    }
}


